import json
import os
from PyQt5.QtWidgets import (
    QHBoxLayout,
    QVBoxLayout,
    QLabel,
    QStyledItemDelegate,
    QGridLayout,
    QPushButton,
    QFileDialog,
    QComboBox,
    QWidget,
    QGroupBox,
    QAbstractItemView,
    QTableWidgetItem,
    QTableWidget,
    QSpacerItem,
    QSizePolicy,
)
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QEvent, QTimer
from PyQt5.QtGui import QPainter, QPen, QColor, QDoubleValidator
from pydantic import BaseModel, Field, field_validator
from functools import partial
import gui.controls.envs as envs
from ..basic_grid_component import BasicGridComponent
from ..dlg_model_params import DlgModelParams
from ..c_params_table import CParamsTable
from ..dlg_params import DlgParams
from ..utils import Utils
from ..util import util_key_map_special, util_key_map
from ..custom_widget import (
    LineEditFloat,
    LineEditStr,
    LineEditInt,
    SelectBox,
    SelectBox2,
    MultRadioBtnGroupNew,
)
from .. import message_box as MBox
from .materialSelect import DlgMaterialSelect
import copy
from enum import Enum

typeMap = {
    "Laminate": "层合板",
    "Honeycomb": "蜂窝",
    "Metal": "金属",
}


class FloatValidatorDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super(FloatValidatorDelegate, self).__init__(parent)
        self.validator = QDoubleValidator(self)

    def createEditor(self, parent, option, index):
        editor = super(FloatValidatorDelegate, self).createEditor(parent, option, index)
        editor.setValidator(self.validator)
        return editor


class CustomTable2(CParamsTable):

    def __init__(self, rows=0, columns=2, parent=None):
        super(CustomTable2, self).__init__(rows=rows, columns=columns, parent=parent)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.verticalHeader().setVisible(True)


class CustomTable(CParamsTable):

    def __init__(self, rows=0, columns=2, parent=None):
        super(CustomTable, self).__init__(rows=rows, columns=columns, parent=parent)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.verticalHeader().setVisible(True)

    #         self.setStyleSheet(
    #             """
    #     QTableWidget::item:selected {
    #         background: palette(Highlight);
    #         color: palette(HighlightedText);
    #     }
    #     QWidget {
    #         background: transparent;
    #     }
    # """
    #         )

    def addSelectRow(self, options, value=None):
        rowIdx = self.rowCount()
        self.insertRow(rowIdx)
        combo = SelectBox2(options=options, onChanged=self.on_combo_changed)
        combo.setValue(value)
        layout = QVBoxLayout()
        layout.addWidget(combo)
        cell_widget = QWidget()
        cell_widget.setLayout(layout)
        # cell_widget.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setCellWidget(rowIdx, 0, cell_widget)
        if value:
            combo.setValue(value)

    def fillSelectRow(self, row, value):
        wrapWidget = self.cellWidget(row, 0)
        if wrapWidget:
            wrapLayout = wrapWidget.layout()
            selectBox = wrapLayout.itemAt(0).widget()
            selectBox.setValue(value)

    def on_combo_changed(self, text, combo):
        # table = self.parent().parent().parent()
        # if not isinstance(table, QTableWidget):
        #     return
        # curRow = -1
        # for row in range(self.rowCount()):
        #     cell_widget = self.cellWidget(row, 0)
        #     if cell_widget and cell_widget.findChild(QComboBox) == combo:
        #         print(f"当前行: {row}, 选择的值: {text}")
        #         # 处理值唯一性检查
        #         curRow = -1 = row
        #         break
        titleList = []
        for row in range(self.rowCount()):
            cell_widget = self.cellWidget(row, 0)
            if cell_widget:
                select = cell_widget.findChild(QComboBox)
                if select == combo:
                    continue
                else:
                    titleList.append(select.currentText())

        if text in titleList:
            MBox.warning("请勿重复选择")
            combo.setCurrentIndex(-1)

    # def addRow():


class DlgOptimization(DlgModelParams):
    optimization_save_signal = pyqtSignal(object)

    def __init__(self, parent=None):
        super(DlgOptimization, self).__init__(parent)

        self.setWindowTitle("优化方法")
        self.pBtnOK.setFocusPolicy(Qt.NoFocus)
        self.pBtnCancel.setFocusPolicy(Qt.NoFocus)

        # paramsModeingData = Utils.read_json("data/ipimas/uis/ParamsModeling.json")
        # data = Utils.read_json("data/ipimas/uis/Optimization.json")

        # compositePlyData = Utils.read_json("data/ipimas/uis/compositePly.json")
        # if compositePlyData:
        #     self.compositePlyData = compositePlyData

        # adhesiveLayerData = Utils.read_json("data/ipimas/uis/adhesiveLayer.json")
        # if adhesiveLayerData:
        #     self.adhesiveLayerData = adhesiveLayerData

        # honeycombCoreData = Utils.read_json("data/ipimas/uis/honeycombCore.json")
        # if honeycombCoreData:
        #     self.honeycombCoreData = honeycombCoreData

        # self.materialList = self.transform(
        #     self.compositePlyData, self.honeycombCoreData, self.adhesiveLayerData
        # )
        # self.materialDict = self.transformToDict(self.materialList)
        # print("---", self.materialDict)

        self.widgetDict = {}
        # self.setMinimumWidth(800)
        self.resize(-1, 700)
        layout = QVBoxLayout()
        componentLayout = QVBoxLayout()

        formLayout = self.createLFormLayout()

        lapWidthLayers = self.createLapWidthLayersTable("搭接宽度", "lapWidth")
        anglelayersBox = QGroupBox("修补层角度")
        anglelayers = QHBoxLayout()
        anglelayersBox.setLayout(anglelayers)
        topAngleLayers = self.createTopAngleLayersTable("上铺层", "topAngle")
        botAngleLayers = self.createTopAngleLayersTable("下铺层", "bottomAngle")
        anglelayers.addWidget(topAngleLayers)
        anglelayers.addWidget(botAngleLayers)
        replaceCoreLayers = self.createReplaceCoreLayersTable(
            "替换芯子材料属性", "replaceCore"
        )

        componentLayout.addLayout(formLayout)
        componentLayout.addWidget(lapWidthLayers)
        componentLayout.addWidget(anglelayersBox)
        componentLayout.addWidget(replaceCoreLayers)

        layout.addLayout(componentLayout)

        self.vbox.insertLayout(0, layout)

        envs.ipimas_updateParamsodeling = (
            lambda type, *args, **kwargs: self.__update_model(type, *args, **kwargs)
        )
        envs.ipimas_updateMaterialLibrary = (
            lambda *args, **kwargs: self.__libraryChange(*args, **kwargs)
        )

        # self.update_UI(paramsModeingData, data)

    def __libraryChange(self, *args, **kwargs):
        if self.isVisible():
            QTimer.singleShot(500, self.delayed_action)

    def delayed_action(self):
        MBox.warning("材料库已更新，请重新打开优化方法设置窗口以获取最新数据！")

    def __update_model(self, data, *args, **kwargs):
        if self.isVisible():
            paramsModeingData = {}
            for key, value in data.items():
                paramsModeingData[util_key_map.valueMap.get(key) or ""] = value

            topRepairPly = paramsModeingData.get("topRepairPlyTable") or []
            topCount = len(topRepairPly)
            self.widgetDict["topAngleTable"].setRowCount(topCount)
            self.widgetDict["topAngleTable"].setCellCenter()

            botRepairPly = paramsModeingData.get("bottomRepairPlyTable") or []
            botCount = len(botRepairPly)
            self.widgetDict["bottomAngleTable"].setRowCount(botCount)
            self.widgetDict["bottomAngleTable"].setCellCenter()

            if paramsModeingData.get("damageType") != "贯穿式损伤":
                self.widgetDict["bottomAngleTable"].setEnabled(False)
            else:
                self.widgetDict["bottomAngleTable"].setEnabled(True)

    def init(self, data, otherData, opt):
        self.materialList = self.transform2(opt)
        self.materialDict = self.transformToDict(self.materialList)
        self.update_UI(otherData, data)

    def update_UI(self, originData, fillOriginData={}):

        fillData = {}
        data = {}
        for key, value in fillOriginData.items():
            fillData[util_key_map_special.valueMap.get(key) or ""] = value
        for key, value in originData.items():
            data[util_key_map.valueMap.get(key) or ""] = value

        file = "data/ipimas/uis/Optimization2.json"
        Utils.write_json(file, fillData)

        # 根据上铺层修补层数 设置上铺修补层角度数量
        if data:
            topRepairPly = data.get("topRepairPlyTable") or []
            topCount = len(topRepairPly)
            self.widgetDict["topAngleTable"].setRowCount(topCount)

            botRepairPly = data.get("bottomRepairPlyTable") or []
            botCount = len(botRepairPly)
            self.widgetDict["bottomAngleTable"].setRowCount(botCount)

            if data.get("damageType") != "贯穿式损伤":
                self.widgetDict["bottomAngleTable"].setEnabled(False)
            else:
                self.widgetDict["bottomAngleTable"].setEnabled(True)
        for key, value in fillData.items():
            if (
                key != "lapWidthTable"
                and key != "topAngleTable"
                and key != "bottomAngleTable"
                and key != "replaceCoreTable"
            ):
                self.widgetDict[key].setValue(value)

        lapWidth = fillData.get("lapWidthTable") or []
        topAngle = fillData.get("topAngleTable") or []
        botAngle = fillData.get("bottomAngleTable") or []
        replaceCore = fillData.get("replaceCoreTable") or []
        self.replaceCore = self.transform2(replaceCore)

        for i, value in enumerate(lapWidth):
            if i < 3:
                item = self.widgetDict["lapWidthTable"].item(0, i)
                if item:
                    item.setText(str(value))
                    item.setTextAlignment(Qt.AlignCenter)
                else:
                    item = QTableWidgetItem(str(value))
                    item.setTextAlignment(Qt.AlignCenter)
                    self.widgetDict["lapWidthTable"].setItem(0, i, item)

        for i, value in enumerate(topAngle):
            item = self.widgetDict["topAngleTable"].item(i, 0)
            if item:
                item.setText(str(value))
                item.setTextAlignment(Qt.AlignCenter)
            else:
                item = QTableWidgetItem(str(value))
                item.setTextAlignment(Qt.AlignCenter)
                self.widgetDict["topAngleTable"].setItem(i, 0, item)

        for i, value in enumerate(botAngle):
            item = self.widgetDict["bottomAngleTable"].item(i, 0)
            if item:
                item.setText(str(value))
                item.setTextAlignment(Qt.AlignCenter)
            else:
                item = QTableWidgetItem(str(value))
                item.setTextAlignment(Qt.AlignCenter)
                self.widgetDict["bottomAngleTable"].setItem(i, 0, item)

        self.widgetDict["replaceCoreTable"].setRowCount(0)
        for i, value in enumerate(self.replaceCore):
            self.widgetDict["replaceCoreTable"].addSelectRow(
                self.materialList, value.get("title")
            )
        self.widgetDict["replaceCoreTable"].resizeRowsToContents()

        self.widgetDict["lapWidthTable"].setCellCenter()
        self.widgetDict["topAngleTable"].setCellCenter()
        self.widgetDict["bottomAngleTable"].setCellCenter()

    def createLFormLayout(self):
        gridLayout = QGridLayout()
        gridLayout.setVerticalSpacing(8)
        gridList = [
            {
                "name": "method",
                "type": "select",
                "title": "优化功能",
                "options": [
                    {"value": "几何参数优化", "title": "几何参数优化"},
                    {"value": "复合材料铺层优化", "title": "复合材料铺层优化"},
                    {"value": "材料-结构协同优化", "title": "材料-结构协同优化"},
                ],
            },
            {
                "name": "algorithm",
                "type": "select",
                "title": "优化算法",
                "options": [
                    {"value": "梯度类算法SQP", "title": "梯度类算法SQP"},
                    {"value": "启发式算法GA", "title": "启发式算法GA"},
                    {"value": "CMA-ES", "title": "CMA-ES"},
                    {"value": "贝叶斯优化方法", "title": "贝叶斯优化方法"},
                ],
            },
            {
                "name": "goal",
                "type": "select",
                "title": "优化目标",
                "options": [
                    {"value": "最大主应变最小化", "title": "最大主应变最小化"},
                    {"value": "极限承载力最大化", "title": "极限承载力最大化"},
                ],
            },
            {
                "name": "constraints",
                "type": "multRadio",
                "title": "约束条件",
                "options": [
                    {"value": "Weight", "title": "质量"},
                    {"value": "Displacement", "title": "位移"},
                ],
            },
        ]
        self.createParameterUI(gridList, gridLayout)
        return gridLayout

    def createLapWidthLayersTable(self, title, name):
        groupBox = QGroupBox(title)
        layout = QVBoxLayout()
        groupBox.setLayout(layout)
        table = CustomTable2(rows=1, columns=3)
        self.widgetDict[f"{name}Table"] = table
        table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
        table.setItemDelegateForColumn(1, FloatValidatorDelegate(table))
        table.setItemDelegateForColumn(2, FloatValidatorDelegate(table))
        table.setHorizontalHeaderLabels(["下限", "初始值", "上限"])
        layout.addWidget(table)
        self.setTableFixeHeight(table)
        table.setCellCenter()
        return groupBox

    def createTopAngleLayersTable(self, title, name):
        groupBox = QGroupBox(title)
        layout = QVBoxLayout()
        groupBox.setLayout(layout)
        table = CustomTable2(rows=0, columns=1)
        # table.setMinimumHeight(160)
        self.widgetDict[f"{name}Table"] = table
        table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
        table.setHorizontalHeaderLabels(["可选角度"])
        layout.addWidget(table)
        table.setCellCenter()
        # table.addRow()
        return groupBox

    def createReplaceCoreLayersTable(self, title, name):

        groupBox = QGroupBox(title)
        layout = QVBoxLayout()
        btnLayout = QHBoxLayout()
        layout.addLayout(btnLayout)
        btnLayout.addStretch()
        addBtn = QPushButton("新增")
        addBtn.clicked.connect(self.addRow)
        delBtn = QPushButton("删除")
        delBtn.clicked.connect(self.delRow)
        batchAddBtn = QPushButton("批量添加")
        batchAddBtn.clicked.connect(self.batchAdd)
        btnLayout.addWidget(addBtn)
        btnLayout.addWidget(delBtn)
        btnLayout.addWidget(batchAddBtn)
        groupBox.setLayout(layout)
        table = CustomTable(rows=0, columns=1)
        # table.setMinimumHeight(160)
        self.widgetDict[f"{name}Table"] = table
        table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
        table.setHorizontalHeaderLabels(["材料名称/牌号"])
        table.setCellCenter()
        layout.addWidget(table)

        return groupBox

    def addRow(self):
        self.widgetDict["replaceCoreTable"].addSelectRow(self.materialList)
        self.widgetDict["replaceCoreTable"].resizeRowsToContents()

    def delRow(self):
        self.widgetDict["replaceCoreTable"].delete_selected_row()

    def batchAdd(self):

        selectedList = self.getTableList(
            self.widgetDict["replaceCoreTable"],
            isFloat=False,
            isSignleCol=True,
            isMaterial=True,
        )
        copyDict = copy.deepcopy(self.materialDict)
        for key, data in copyDict.items():
            if key in selectedList:
                data["selected"] = True
            else:
                data["selected"] = False

        if hasattr(self, "dlgMaterialSelect") and self.dlgMaterialSelect:
            self.dlgMaterialSelect.show()
            self.dlgMaterialSelect.init(copyDict)
        else:
            self.dlgMaterialSelect = DlgMaterialSelect(self)
            self.dlgMaterialSelect.materialSelect_save_signal.connect(
                lambda data,: self.materialSelect_save_signal(data)
            )
            self.dlgMaterialSelect.show()
            self.dlgMaterialSelect.init(copyDict)

    def materialSelect_save_signal(self, data):
        res = []
        for key in data:
            # 获取字典的第一个键作为title
            item = self.materialDict.get(key)
            if item:
                title = (
                    (item.get("材料名称") or "")
                    + "-"
                    + (typeMap.get(item.get("材料类型")) or "")
                )
                res.append({"value": title, "title": title, "data": item})
        self.widgetDict["replaceCoreTable"].setRowCount(0)
        for i, value in enumerate(res):
            self.widgetDict["replaceCoreTable"].addSelectRow(
                self.materialList, value.get("title")
            )
        self.widgetDict["replaceCoreTable"].resizeRowsToContents()

    def getTableList(self, table, isFloat=False, isSignleCol=False, isMaterial=False):
        list = []
        for i in range(table.rowCount()):
            if isMaterial:
                wrapWidget = table.cellWidget(i, 0)
                if wrapWidget:
                    wrapLayout = wrapWidget.layout()
                    selectBox = wrapLayout.itemAt(0).widget()
                    list.append(selectBox.getValue())
            elif isSignleCol:
                item = table.item(i, 0)
                if item:
                    if isFloat:
                        list.append(toFloat(item.text()))
                    else:
                        list.append(item.text())
                else:
                    list.append(None)
            else:
                for j in range(table.columnCount()):
                    item = table.item(i, j)
                    if item:
                        if isFloat:
                            list.append(toFloat(item.text()))
                        else:
                            list.append(item.text())
                    else:
                        list.append(None)
        return list

    def createParameterUI(self, list, layout, row=0, col=0):
        for index, item in enumerate(list):
            name = item.get("name")
            index = row + index
            title = item.get("title")
            label = QLabel(title)
            layout.addWidget(label, index, 0 + col)
            if item["type"] == "str":
                self.widgetDict[name] = LineEditStr()
            elif item["type"] == "int":
                self.widgetDict[name] = LineEditInt()
            elif item["type"] == "float":
                self.widgetDict[name] = LineEditFloat()
            elif item["type"] == "select":
                self.widgetDict[name] = SelectBox(
                    options=item["options"], onChanged=item.get("onChanged")
                )
            elif item["type"] == "multRadio":
                self.widgetDict[name] = MultRadioBtnGroupNew(
                    options=item["options"], onChanged=item.get("onChanged")
                )
            layout.addWidget(self.widgetDict[name], index, 1 + col)

    @pyqtSlot()
    def on_pBtnOK_clicked(self):

        valueRes = {}
        for key, widget in self.widgetDict.items():
            if not isinstance(widget, CParamsTable):
                value = widget.getValue()
                valueRes[key] = value

        lapWidthList = self.getTableList(
            self.widgetDict["lapWidthTable"], isFloat=True, isSignleCol=False
        )
        topAngleList = self.getTableList(
            self.widgetDict["topAngleTable"], isFloat=True, isSignleCol=True
        )
        botAngleList = self.getTableList(
            self.widgetDict["bottomAngleTable"], isFloat=True, isSignleCol=True
        )
        replaceCoreValueList = self.getTableList(
            self.widgetDict["replaceCoreTable"],
            isFloat=False,
            isSignleCol=True,
            isMaterial=True,
        )
        replaceCoreList = []
        if replaceCoreValueList:
            for value in replaceCoreValueList:
                replaceCoreList.append(self.materialDict.get(value))

        valueRes["lapWidthTable"] = lapWidthList
        valueRes["topAngleTable"] = topAngleList
        valueRes["bottomAngleTable"] = botAngleList
        valueRes["replaceCoreTable"] = replaceCoreList

        if self.check_empty_list_values(lapWidthList):
            return MBox.warning("请补全搭接宽度数据")
        if self.check_empty_list_values(topAngleList):
            return MBox.warning("请补全上铺层修补层角度数据")
        if self.check_empty_list_values(botAngleList):
            return MBox.warning("请补全下铺层修补层角度数据")

        res = {}
        for key, value in valueRes.items():
            res[util_key_map_special.keyMap.get(key) or ""] = value

        file = "data/ipimas/uis/Optimization.json"
        Utils.write_json(file, res)
        self.close()
        self.optimization_save_signal.emit(res)

    def check_empty_list_values(self, data_list):
        if data_list:
            for value in data_list:
                if value == "" or value is None:
                    return True
        return False

    def closeEvent(self, event):
        DlgOptimization._shown = False  # 使用类名访问类属性
        DlgOptimization._instance = None  # 清除实例引用
        event.accept()

    def setTableFixeHeight(self, table):
        total_height = table.rowHeight(0)
        horizontal_header_height = table.horizontalHeader().height()
        table.setFixedHeight(total_height + horizontal_header_height)

    def transform(self, data1, data2, data3):
        result1 = []
        result2 = []
        result3 = []
        for item in data1:
            # 获取字典的第一个键作为title
            if item:
                title = (
                    (item.get("材料名称") or "")
                    + "-"
                    + (typeMap.get(item.get("材料类型")) or "")
                )
                result1.append({"value": title, "title": title, "data": item})
        for item in data2:
            # 获取字典的第一个键作为title
            if item:
                title = (
                    (item.get("材料名称") or "")
                    + "-"
                    + (typeMap.get(item.get("材料类型")) or "")
                )
                result2.append({"value": title, "title": title, "data": item})
        for item in data3:
            # 获取字典的第一个键作为title
            if item:
                title = (
                    (item.get("材料名称") or "")
                    + "-"
                    + (typeMap.get(item.get("材料类型")) or "")
                )
                result3.append({"value": title, "title": title, "data": item})
        return result1 + result2 + result3

    def transform2(self, data):

        result = []
        for item in data:
            if item:
                title = (
                    (item.get("材料名称") or "")
                    + "-"
                    + (typeMap.get(item.get("材料类型")) or "")
                )
                result.append({"value": title, "title": title, "data": item})
        return result

    def transformToDict(self, dataList):
        dataDict = {}
        for item in dataList:
            if item:
                # title = (item.get("材料名称") or "")  + "-" + (item.get('材料类型') or "")
                title = item.get("title") or ""
                dataDict[title] = item.get("data")
        return dataDict


def toFloat(value):
    try:
        return float(value)
    except ValueError:
        return None
